package com.javen.zookeeper;

import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author Javen
 */
@Component
@Slf4j
public class ZookeeperApi {

    @Autowired
    private ZooKeeper zooKeeper;


    /**
     * 判断指定节点是否存在
     *
     * @param path      节点 path
     * @param needWatch 是否 watcher
     * @return {@link Stat}
     */
    public Stat exists(String path, boolean needWatch) {
        try {
            return zooKeeper.exists(path, needWatch);
        } catch (Exception e) {
            log.error(String.format("断指定节点是否存在异常 %s", path), e);
            return null;
        }
    }

    /**
     * 检测结点是否存在 并设置监听事件
     * 三种监听类型:创建，删除，更新
     *
     * @param path    节点 path
     * @param watcher 传入指定的监听类
     * @return {@link Stat}
     */
    public Stat exists(String path, Watcher watcher) {
        try {
            return zooKeeper.exists(path, watcher);
        } catch (Exception e) {
            log.error(String.format("断指定节点是否存在异常 %s", path), e);
            return null;
        }
    }

    /**
     * 创建持久化节点
     *
     * @param path 节点 path
     * @param data 数据
     * @return 是否创建成功
     */
    public boolean createNode(String path, String data) {
        try {
            return createNode(path, data, CreateMode.PERSISTENT);
        } catch (Exception e) {
            log.error(String.format("创建持久化节点异常 %s %s", path, data), e);
            return false;
        }
    }

    /**
     * 创建临时节点
     *
     * @param path 节点 path
     * @param data 数据
     * @return 是否创建成功
     */
    public boolean createNodeByEphemeral(String path, String data) {
        try {
            return createNode(path, data, CreateMode.EPHEMERAL);
        } catch (Exception e) {
            log.error(String.format("创建临时节点异常 %s %s", path, data), e);
            return false;
        }
    }

    /**
     * 创建节点
     *
     * @param path       节点 path
     * @param data       数据
     * @param createMode {@link CreateMode} 节点模式
     * @return 是否创建成功
     */
    public boolean createNode(String path, String data, CreateMode createMode) {
        try {
            zooKeeper.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode);
            return true;
        } catch (Exception e) {
            log.error(String.format("创建 %s 节点异常 %s %s", createMode, path, data), e);
            return false;
        }
    }


    /**
     * 修改持久化节点
     *
     * @param path 节点 path
     * @param data 数据
     * @return 是否更新成功
     */
    public boolean updateNode(String path, String data) {
        try {
            Stat stat = exists(path, false);
            // version参数指定要更新的数据的版本, 如果 version 和真实的版本不同, 更新操作将失败。 指定version为-1则忽略版本检查
            zooKeeper.setData(path, data.getBytes(), stat.getVersion());
            return true;
        } catch (Exception e) {
            log.error(String.format("修改持久化节点异常 %s %s", path, data), e);
            return false;
        }
    }

    /**
     * 删除持久化节点
     *
     * @param path 节点 path
     * @return 是否删除成功
     */
    public boolean deleteNode(String path) {
        try {
            Stat stat = exists(path, false);
            zooKeeper.delete(path, stat.getVersion());
            return true;
        } catch (Exception e) {
            log.error(String.format("删除持久化节点异常 %s", path), e);
            return false;
        }
    }

    /**
     * 获取当前节点的子节点(不包含孙子节点)
     *
     * @param path 父节点 path
     * @return 子节点列表
     */
    public List<String> getChildren(String path) throws KeeperException, InterruptedException {
        return zooKeeper.getChildren(path, false);
    }

    /**
     * 获取指定节点的值
     *
     * @param path 节点 path
     * @return 节点值
     */
    public String getData(String path, Watcher watcher) {
        try {
            Stat stat = exists(path, false);
            byte[] bytes = zooKeeper.getData(path, watcher, stat);
            return new String(bytes);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

